package com.zh.service.impl;

import cn.dev33.satoken.stp.SaTokenInfo;
import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.bean.copier.CopyOptions;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.digest.DigestUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.zh.common.DeleteRequest;
import com.zh.common.ErrorCode;
import com.zh.constant.CommonConstant;
import com.zh.constant.UserConstant;
import com.zh.domain.dto.user.UserAddRequest;
import com.zh.domain.dto.user.UserQueryRequest;
import com.zh.domain.dto.user.UserUpdateRequest;
import com.zh.domain.entity.Team;
import com.zh.domain.entity.TeamUser;
import com.zh.domain.entity.User;
import com.zh.domain.vo.user.UserVO;
import com.zh.exception.BusinessException;
import com.zh.exception.ThrowUtils;
import com.zh.mapper.TeamMapper;
import com.zh.mapper.TeamUserMapper;
import com.zh.mapper.UserMapper;
import com.zh.service.UserService;
import com.zh.utils.SqlUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

import static com.zh.constant.UserConstant.*;

/**
 * 用户服务实现
 */
@Service
@Slf4j
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {

    private final UserMapper userMapper;
    private final TeamMapper teamMapper;

    private final TeamUserMapper teamUserMapper;

    public UserServiceImpl(UserMapper userMapper, TeamMapper teamMapper, TeamUserMapper teamUserMapper) {
        this.userMapper = userMapper;
        this.teamMapper = teamMapper;
        this.teamUserMapper = teamUserMapper;
    }

    @Override
    @Transactional
    public long userRegister(String username, String password, String rePassword, String nickname, String name) {
        // 1. 校验
        ThrowUtils.throwIf(StringUtils.isAnyEmpty(username, name, password, rePassword), ErrorCode.PARAMS_ERROR);
        ThrowUtils.throwIf(username.length() < 6, ErrorCode.PARAMS_ERROR, "用户账号不能少于6位");
        ThrowUtils.throwIf(username.length() > 32, ErrorCode.PARAMS_ERROR, "用户账号不能大于32位");
        ThrowUtils.throwIf(password.length() < 8 || rePassword.length() < 8, ErrorCode.PARAMS_ERROR, "用户密码不能少于8位");
        ThrowUtils.throwIf(password.length() > 32 || rePassword.length() > 32, ErrorCode.PARAMS_ERROR, "用户密码不能大于32位");
        ThrowUtils.throwIf(!password.equals(rePassword), ErrorCode.PARAMS_ERROR, "两次密码不一致");
        synchronized (username.intern()) {
            // 账户查复
            LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
            queryWrapper.eq(User::getUsername, username);
            long count = this.baseMapper.selectCount(queryWrapper);
            ThrowUtils.throwIf(count > 0, ErrorCode.PARAMS_ERROR, "账号已存在");
            // 2. 加密
            String randomString = RandomUtil.randomString(16);
            String randomSalt = SALT + randomString;
            String encryptPassword = DigestUtil.sha256Hex(randomSalt + password);
            // 3. 插入数据
            User user = new User();
            user.setName(name);
            if (StrUtil.isNotEmpty(nickname)) {
                user.setNickname(nickname);
            }
            user.setUsername(username);
            user.setPassword(encryptPassword);
            user.setSalt(randomSalt);
            user.setAvatar(DEFAULT_AVATAR);
            user.setType(TYPE_CUSTOM);
            user.setRoles(List.of(USER));
            user.setCreateBy(1);
            user.setCreateAt(LocalDateTime.now());
            user.setUpdateBy(1);
            user.setUpdateAt(LocalDateTime.now());
            boolean saveResult = this.save(user);
            if (!saveResult) {
                throw new BusinessException(ErrorCode.SYSTEM_ERROR, "注册失败，服务器异常错误");
            }
            return user.getId();
        }
    }

    @Override
    public UserVO userLogin(String username, String password, HttpServletRequest request, HttpServletResponse response) {
        // 1. 校验参数
        if (StringUtils.isAnyBlank(username, password) || username.length() < 6 || username.length() > 32 || password.length() < 8 || password.length() > 32) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "用户名或密码错误");
        }
        // 2. 查询用户是否存在
        User user = this.getOne(new LambdaQueryWrapper<User>().eq(User::getUsername, username));
        ThrowUtils.throwIf(ObjectUtil.isNull(user), ErrorCode.PARAMS_ERROR, "用户不存在", e -> log.info("user login failed, username cannot match password"));
        //3.查询用户状态
        ThrowUtils.throwIf(0 == user.getStatus(), ErrorCode.FORBIDDEN_ERROR, "该账户已封禁,请联系管理员", e -> {
            log.warn("user login failed, user has been banned!:{}", user.getId());
            response.setStatus(403);
        });
        //4. 校验密码
        String salt = user.getSalt();
        String encryptPassword = DigestUtil.sha256Hex(salt + password);
        ThrowUtils.throwIf(!encryptPassword.equals(user.getPassword()), ErrorCode.PARAMS_ERROR, "用户或密码错误");
        boolean loginSuccess = username.equals(user.getUsername()) && encryptPassword.equals(user.getPassword());
        ThrowUtils.throwIf(!loginSuccess, ErrorCode.PARAMS_ERROR, "用户或密码错误", e -> {
            log.warn("user login failed, username cannot match password:{}", user.getId());
            response.setStatus(401);
        });
        // 5. 登录成功返回token
        StpUtil.login(user.getId());
        SaTokenInfo tokenInfo = StpUtil.getTokenInfo();
        String tokenName = tokenInfo.getTokenName();
        String tokenValue = tokenInfo.getTokenValue();
        response.setHeader(tokenName, tokenValue);
        return user.toUserVo();
    }


    @Override
    public LambdaQueryWrapper<User> getQueryWrapper(UserQueryRequest userQueryRequest) {
        if (userQueryRequest == null) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "请求参数为空");
        }
        Long id = userQueryRequest.getId();
        String username = userQueryRequest.getUsername();
        String nickname = userQueryRequest.getNickname();
        String name = userQueryRequest.getName();
        List<String> roles = userQueryRequest.getRoles();
        String type = userQueryRequest.getType();
        String sortField = userQueryRequest.getSortField();
        String sortOrder = userQueryRequest.getSortOrder();
        LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(id != null, User::getId, id);
        queryWrapper.like(StrUtil.isNotBlank(username), User::getUsername, username);
        queryWrapper.like(StrUtil.isNotBlank(nickname), User::getNickname, nickname);
        queryWrapper.like(StrUtil.isNotBlank(name), User::getName, name);
        queryWrapper.eq(StrUtil.isNotBlank(type), User::getType, type);
        if (CollUtil.isNotEmpty(roles)) {
            roles.forEach(role -> queryWrapper.like(User::getRoles, role));
        }
        queryWrapper.orderBy(SqlUtils.validSortField(sortField), sortOrder.equals(CommonConstant.SORT_ORDER_DESC), User::getUpdateBy);
        return queryWrapper;
    }

    @Override
    @Transactional
    public Integer add(UserAddRequest userAddRequest, HttpServletResponse response) {
        //1.当前用户校验:超级管理员直接添加成功,团队长必须指定自己的团队id
        int loginId = StpUtil.getLoginIdAsInt();
        synchronized (String.valueOf(loginId).intern()) {
            List<String> roleList = StpUtil.getRoleList();
            Integer teamId = userAddRequest.getTeamId();
            boolean teamChecked = teamCheck(loginId, roleList, teamId);
            ThrowUtils.throwIf(!teamChecked, ErrorCode.OPERATION_ERROR, "您仅可在自己的团队中添加成员");
            User user = new User();
            BeanUtils.copyProperties(userAddRequest, user);
            //2.校验用户名是否重复
            String username = user.getUsername();
            LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
            queryWrapper.eq(User::getUsername, username);
            boolean exists = userMapper.selectCount(queryWrapper) > 0;
            ThrowUtils.throwIf(exists, ErrorCode.SUCCESS, "用户名重复");
            String randomString = RandomUtil.randomString(16);
            String randomSalt = SALT + randomString;
            String encryptPassword = DigestUtil.sha256Hex((randomSalt + DEFAULT_PASSWORD));
            user.setPassword(encryptPassword);
            user.setSalt(randomSalt);
            user.setAvatar(UserConstant.DEFAULT_AVATAR);
            user.setRoles(List.of(UserConstant.USER));
            user.setType(TYPE_SPECIAL);
            user.setCreateAt(LocalDateTime.now());
            user.setCreateBy(StpUtil.getLoginIdAsInt());
            user.setUpdateAt(LocalDateTime.now());
            user.setUpdateBy(StpUtil.getLoginIdAsInt());
            boolean result = this.save(user);
            ThrowUtils.throwIf(!result, ErrorCode.OPERATION_ERROR, "系统错误", e -> log.error("用户创建失败,插入数据库失败", e));
            Integer userId = user.getId();
            //3.插入team_user表
            if (null != teamId) {
                TeamUser teamUser = new TeamUser();
                teamUser.setTeamId(teamId);
                teamUser.setUserId(userId);
                teamUser.setRole(0);
                try {
                    teamUserMapper.insert(teamUser);
                } catch (DuplicateKeyException e) {
                    log.error("插入team_user表失败,数据库唯一索引冲突", e);
                    ThrowUtils.throwIf(ErrorCode.OPERATION_ERROR, "请勿重复加入");
                }
            }
            return userId;
        }
    }

    @Override
    @Transactional
    public synchronized boolean removeUserById(DeleteRequest deleteRequest, HttpServletResponse response) {
        //1.当前用户校验:超级管理员直接删除成功,团队长必须指定自己的团队id
        int loginId = StpUtil.getLoginIdAsInt();
        List<String> roleList = StpUtil.getRoleList();
        Integer teamId = deleteRequest.getTeamId();
        Integer userId = deleteRequest.getId();
        if (CollectionUtil.contains(roleList, SUPER_ADMIN)) {
            return this.removeById(userId);
        }
        //2.判断是否为团队长,若是,判断是否拥有该团队,若是,判断是否为special号
        boolean teamCheck = teamCheck(loginId, roleList, teamId);
        ThrowUtils.throwIf(!teamCheck, ErrorCode.OPERATION_ERROR, "您仅可删除自己创建的团队成员");
        LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(User::getId, userId);
        User user = this.getOne(queryWrapper);
        String type = user.getType();
        //2.自建号只有超管能删
        ThrowUtils.throwIf(TYPE_CUSTOM.equals(type) && !CollectionUtil.contains(roleList, SUPER_ADMIN), ErrorCode.FORBIDDEN_ERROR, "您仅可删除自己创建的团队成员");
        //3.若不是超管 判断该用户是否属于该团队
        LambdaQueryWrapper<TeamUser> qw = new LambdaQueryWrapper<>();
        qw.eq(teamId != null, TeamUser::getTeamId, teamId);
        qw.eq(TeamUser::getUserId, userId);
        boolean exists = teamUserMapper.selectCount(qw) > 0;
        ThrowUtils.throwIf(!exists, ErrorCode.FORBIDDEN_ERROR, "您仅可删除自己创建的团队成员");
        //4.删除用户
        boolean success = this.removeById(userId);
        //5.从团队中删除(如果有)
        if (null != teamId) {
            LambdaQueryWrapper<TeamUser> wrapper = new LambdaQueryWrapper<>();
            wrapper.eq(TeamUser::getTeamId, teamId)
                    .eq(TeamUser::getUserId, userId);
            teamUserMapper.delete(wrapper);
        }
        return success;
    }

    @Override
    @Transactional
    public boolean updateUser(UserUpdateRequest userUpdateRequest) {
        //1.判断是否本人,或者登录者是否为超管,是直接更新,否则校验团队所属
        int loginId = StpUtil.getLoginIdAsInt();
        Integer targetId = userUpdateRequest.getId();

        User user = new User();
//        boolean own = loginId == userUpdateRequest.getId();
//
//        List<String> roleList = StpUtil.getRoleList();
//        Integer teamId = userUpdateRequest.getTeamId();
//        boolean teamChecked = teamCheck(loginId, roleList, teamId);

        //2.是否要更新密码?
        String password = userUpdateRequest.getPassword();
        String rePassword = userUpdateRequest.getRePassword();
        boolean updatePassword = false;
        if (!StringUtils.isAnyEmpty(password, rePassword)) {
            ThrowUtils.throwIf(!password.equals(rePassword), ErrorCode.PARAMS_ERROR, "两次密码不一致");
            String randomStr = RandomUtil.randomString(16);
            String randomSalt = SALT + randomStr;
            String encryptPassword = DigestUtil.sha256Hex(SALT + randomStr + password);
            user.setSalt(randomSalt);
            user.setPassword(encryptPassword);
            updatePassword = true;
        }
        //3.校验通过执行更新

        BeanUtil.copyProperties(userUpdateRequest, user, CopyOptions.create()
                .setIgnoreNullValue(true) // 忽略null值
                .setFieldValueEditor((fieldName, fieldValue) -> {
                    // 忽略空字符串
                    if (fieldValue instanceof String && ((String) fieldValue).isEmpty()) {
                        return null;
                    }
                    //忽略password
                    if ("password".equals(fieldName)) {
                        return null;
                    }
                    return fieldValue;
                }));
        user.setUpdateAt(LocalDateTime.now());
        user.setUpdateBy(loginId);
        user.setUpdateAt(LocalDateTime.now());
        user.setUpdateBy(loginId);
        boolean success = this.updateById(user);
        if (updatePassword && success) {
            StpUtil.kickout(targetId);
        }
        return success;
    }

    @Override
    public UserVO getUserVoById(Integer id) {
        User user = this.getById(id);
        return user.toUserVo();
    }

    @Override
    public List<UserVO> listUserVoByIds(List<Integer> ids) {
        if (CollectionUtil.isEmpty(ids)) {
            return new ArrayList<>();
        }
        List<User> users = this.listByIds(ids);
        if (CollectionUtil.isNotEmpty(users)) {
            return users.stream().map(User::toUserVo).collect(Collectors.toList());
        }
        return new ArrayList<>();
    }

    private boolean teamCheck(int loginId, List<String> roleList, Integer teamId) {
        // 如果用户是管理员且 teamId 为空，直接返回 false
        if (CollectionUtil.contains(roleList, UserConstant.ADMIN) && teamId == null) {
            return false;
        }

        // 如果用户不是管理员，直接返回 true
        if (!CollectionUtil.contains(roleList, UserConstant.ADMIN)) {
            return true;
        }

        // 判断团队是否属于本人
        LambdaQueryWrapper<Team> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(Team::getOwnerId, loginId);
        List<Integer> teamIds = teamMapper.selectList(queryWrapper).stream()
                .map(Team::getId)
                .collect(Collectors.toList());

        // 如果 teamId 不在用户拥有的团队列表中
        return teamIds.contains(teamId);
    }
}
